home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-06-23 | 55.4 KB | 1,891 lines |
- /*******************************************************************
- Copyright © 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").
- All rights reserved.
-
- This software may be copied and used solely for developing products for
- the Palm Computing platform and for archival and backup purposes. Except
- for the foregoing, no part of this software may be reproduced or transmitted
- in any form or by any means or used to make any derivative work (such as
- translation, transformation or adaptation) without express written consent
- from 3Com.
-
- 3Com reserves the right to revise this software and to make changes in content
- from time to time without obligation on the part of 3Com to provide notification
- of such revision or changes.
- 3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS
- OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE. THE SOFTWARE IS PROVIDED ON AN
- "AS IS" BASIS. 3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED,
- EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES,
- TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
- SATISFACTORY QUALITY.
-
- TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS
- ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR
- DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF
- ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION
- OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE,
- EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
- 3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and
- Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.
-
- IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND
- DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT
- ACCOMPANYING THE COMPACT DISK.
-
- *-------------------------------------------------------------------
- * FileName:
- * AddressMgr.c
- *
- * Description:
- * Address Manager routines
- *
- * History:
- * 1/9/95 rsf - Created
- * 7/22/96 rsf - Added the Address Lookup routines. These calls
- * perform the data searching needed by the app to
- * support the lookup launch notification.
- *
- *******************************************************************/
-
-
- // Set this to get to private database defines
- #define __ADDRMGR_PRIVATE__
-
- #include <Pilot.h>
- #include "CharAttr.h"
- #include "AddressDB.h"
-
-
- // Extract the bit at position index from bitfield. 0 is the high bit.
- #define BitAtPosition(pos) ((ULong)1 << (pos))
- #define GetBitMacro(bitfield, index) ((bitfield) & BitAtPosition(index))
- #define SetBitMacro(bitfield, index) ((bitfield) |= BitAtPosition(index))
- #define RemoveBitMacro(bitfield, index) ((bitfield) &= ~BitAtPosition(index))
-
-
-
- #define sortKeyFieldBits (BitAtPosition(name) | \
- BitAtPosition(firstName) | \
- BitAtPosition(company))
- //0x70000 // Update this if the sort fields
- // change positions
-
- // The following structure doesn't really exist. The first field
- // varies depending on the data present. However, it is convient
- // (and less error prone) to use when accessing the other information.
- typedef struct {
- AddrOptionsType options; // Display by company or by name
- AddrDBRecordFlags flags;
- unsigned char companyFieldOffset; // Offset from firstField
- char firstField;
- } AddrPackedDBRecord;
-
-
- /************************************************************
- * Private routines used only in this module
- *************************************************************/
-
-
-
- /************************************************************
- *
- * FUNCTION: AddrAppInfoGetPtr
- *
- * DESCRIPTION: Return a locked pointer to the AddrAppInfo or NULL
- *
- * PARAMETERS: dbP - open database pointer
- *
- * RETURNS: locked ptr to the AddrAppInfo or NULL
- *
- * CREATED: 6/13/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- AddrAppInfoPtr AddrAppInfoGetPtr(DmOpenRef dbP)
- {
- UInt cardNo;
- LocalID dbID;
- LocalID appInfoID;
-
- if (DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL))
- return NULL;
- if (DmDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL))
- return NULL;
-
- if (appInfoID == NULL)
- return NULL;
- else
- return MemLocalIDToLockedPtr(appInfoID, cardNo);
-
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrChangeCountry
- *
- * DESCRIPTION: Set the field labels to those appropriate
- * to the current country (based on system preferences).
- *
- * PARAMETERS: application info ptr
- *
- * RETURNS: nothing
- *
- * CREATED: 7/24/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- void AddrChangeCountry(AddrAppInfoPtr appInfoP)
- {
- CountryType countryCurrent;
- AddrAppInfoPtr nilP = 0;
- AddrDBRecordFlags dirtyFieldLabels;
- const Char * cityP = NULL;
- const Char * stateP = NULL;
- const Char * zipCodeP = NULL;
-
-
- // Localize the field labels to the current country
- countryCurrent = (CountryType) PrefGetPreference(prefCountry);
-
-
- #if COUNTRY == COUNTRY_UNITED_STATES
- switch (countryCurrent)
- {
- case cUnitedStates:
- cityP = "City";
- stateP = "State";
- zipCodeP = "Zip Code";
- break;
-
- case cAustralia:
- cityP = "City";
- stateP = "State";
- zipCodeP = "Post Code";
- break;
-
- case cCanada:
- cityP = "City";
- stateP = "Province";
- zipCodeP = "Postal Code";
- break;
-
- case cUnitedKingdom:
- cityP = "Town";
- stateP = "County";
- zipCodeP = "Post Code";
- break;
-
- default:
- break;
- }
- #endif
-
-
- // If labels have changed write them out.
- if (cityP != NULL)
- {
- DmStrCopy(appInfoP, (ULong) nilP->fieldLabels[city], cityP);
- DmStrCopy(appInfoP, (ULong) nilP->fieldLabels[state], stateP);
- DmStrCopy(appInfoP, (ULong) nilP->fieldLabels[zipCode], zipCodeP);
- dirtyFieldLabels.allBits = (appInfoP->dirtyFieldLabels.allBits) |
- BitAtPosition(city) | BitAtPosition(state) | BitAtPosition(zipCode);
- DmWrite(appInfoP, (ULong) &nilP->dirtyFieldLabels, &dirtyFieldLabels, sizeof dirtyFieldLabels);
- }
-
-
- // Record the country.
- DmWrite(appInfoP, (ULong) &nilP->country, &countryCurrent, sizeof(countryCurrent));
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrLocalizeAppInfo
- *
- * DESCRIPTION: Look for localize app info strings and copy
- * them into the app info block.
- *
- * PARAMETERS: application info ptr
- *
- * RETURNS: nothing
- *
- * CREATED: 12/13/95
- *
- * BY: Roger Flores
- *
- * MODIFICATIONS:
- * 10/22/96 roger Set flags when field modified
- *************************************************************/
- static void AddrLocalizeAppInfo(AddrAppInfoPtr appInfoP)
- {
- VoidHand localizedAppInfoH;
- CharPtr localizedAppInfoP;
- AddrAppInfoPtr nilP = 0;
- VoidHand stringsH;
- CharPtr *stringsP;
- int i;
- UInt localRenamedCategories;
- ULong localDirtyFieldLabels;
-
-
- localizedAppInfoH = DmGetResource(appInfoStringsRsc, LocalizedAppInfoStr);
- if (!localizedAppInfoH)
- return;
- localizedAppInfoP = MemHandleLock(localizedAppInfoH);
- stringsH = SysFormPointerArrayToStrings(localizedAppInfoP,
- dmRecNumCategories + addrNumFields + numPhoneLabelsStoredSecond);
- stringsP = MemHandleLock(stringsH);
-
-
- // Copy each category
- localRenamedCategories = appInfoP->renamedCategories;
- for (i = 0; i < dmRecNumCategories; i++)
- {
- if (stringsP[i][0] != '\0')
- {
- DmStrCopy(appInfoP, (ULong) nilP->categoryLabels[i], stringsP[i]);
- SetBitMacro(localRenamedCategories, i);
- }
- }
- DmWrite(appInfoP, (ULong) &nilP->renamedCategories, &localRenamedCategories,
- sizeof(localRenamedCategories));
-
-
- // Copy each field label
- localDirtyFieldLabels = appInfoP->dirtyFieldLabels.allBits;
- for (i = 0; i < (addrNumFields + numPhoneLabelsStoredSecond); i++)
- {
- if (stringsP[i + dmRecNumCategories][0] != '\0')
- {
- DmStrCopy(appInfoP, (ULong) nilP->fieldLabels[i],
- stringsP[i + dmRecNumCategories]);
- SetBitMacro(localDirtyFieldLabels, i);
- }
- }
- DmWrite(appInfoP, (ULong) &nilP->dirtyFieldLabels.allBits, &localDirtyFieldLabels,
- sizeof(localDirtyFieldLabels));
-
-
- MemPtrFree(stringsP);
- MemPtrUnlock(localizedAppInfoP);
- DmReleaseResource(localizedAppInfoH);
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrAppInfoInit
- *
- * DESCRIPTION: Create an app info chunk if missing. Set
- * the strings to a default.
- *
- * PARAMETERS: dbP - open database pointer
- *
- * RETURNS: 0 if successful, errorcode if not
- *
- * CREATED: 1/3/95
- *
- * BY: Roger Flores
- *
- * MODIFICATIONS:
- * 10/22/96 roger Change to init data via code and resources to
- * remove global var use which wasn't always available.
- *************************************************************/
- Err AddrAppInfoInit(DmOpenRef dbP)
- {
- UInt cardNo;
- LocalID dbID;
- LocalID appInfoID;
- Handle h;
- AddrAppInfoPtr appInfoP;
- AddrAppInfoPtr defaultAddrApplicationInfoP;
- int i;
-
-
- appInfoP = AddrAppInfoGetPtr(dbP);
-
- // If there isn't an AddrApplicationInfo make space for one
- if (appInfoP == NULL)
- {
- if (DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL))
- return dmErrInvalidParam;
- if (DmDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL))
- return dmErrInvalidParam;
-
- h = DmNewHandle(dbP, sizeof(AddrAppInfoType));
- if (!h) return dmErrMemError;
-
- appInfoID = MemHandleToLocalID( h);
- DmSetDatabaseInfo(cardNo, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL);
-
- appInfoP = MemHandleLock(h);
- }
-
-
- // Allocate & Clear the app info
- defaultAddrApplicationInfoP = MemPtrNew(sizeof(AddrAppInfoType));
- if (defaultAddrApplicationInfoP == NULL)
- {
- ErrDisplay("Unable to init AddressDB");
- return 1;
- }
-
- MemSet(defaultAddrApplicationInfoP, sizeof(AddrAppInfoType), 0);
-
- // Init the categories
- for (i = 0; i < dmRecNumCategories; i++)
- {
- defaultAddrApplicationInfoP->categoryUniqIDs[i] = i;
- }
- defaultAddrApplicationInfoP->lastUniqID = dmRecNumCategories - 1;
-
- // Set to sort by name
- defaultAddrApplicationInfoP->misc.sortByCompany = false;
-
-
- // copy in the defaults and free the default app info
- DmWrite(appInfoP, 0, defaultAddrApplicationInfoP, sizeof(AddrAppInfoType));
- MemPtrFree(defaultAddrApplicationInfoP);
-
-
- // Try to use localized app info block strings.
- AddrLocalizeAppInfo(appInfoP);
-
- // Localize the field labels to the current country
- AddrChangeCountry(appInfoP);
-
-
- // Unlock
- MemPtrUnlock(appInfoP);
-
- return 0;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrSetFieldLabel
- *
- * DESCRIPTION: Set a field's label and mark it dirty.
- *
- * PARAMETERS: dbP - open database pointer
- * fieldNum - field label to change
- * fieldLabel - new field label to use
- *
- * RETURNS: 0 if successful, errorcode if not
- *
- * CREATED: 6/28/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- void AddrSetFieldLabel(DmOpenRef dbP, UInt fieldNum, CharPtr fieldLabel)
- {
- AddrAppInfoPtr appInfoP;
- AddrAppInfoType copy;
-
-
- ErrFatalDisplayIf(fieldNum >= lastLabel,
- "fieldNum out of range");
-
- // Get a copy of the app info
- appInfoP = AddrAppInfoGetPtr(dbP);
- ErrFatalDisplayIf(appInfoP == NULL,
- "Bad database (invalid or no app info block)");
- MemMove(©, appInfoP, sizeof(copy));
-
- // Make the changes
- StrCopy(copy.fieldLabels[fieldNum], fieldLabel); //lint !e661
- SetBitMacro(copy.dirtyFieldLabels.allBits, fieldNum);
-
- // Write changes to record
- DmWrite(appInfoP, 0, ©, sizeof(copy));
-
- // Unlock app info
- MemPtrUnlock(appInfoP);
- }
-
-
-
- /************************************************************
- *
- * FUNCTION: AddrFindKey
- *
- * DESCRIPTION: Return the next valid key
- *
- * PARAMETERS: database packed record
- * <-> key to use (ptr to string or NULL for uniq ID)
- * <-> which key (incremented for use again, starts at 1)
- * -> sortByCompany
- *
- * RETURNS:
- *
- * CREATED: 1/16/95
- *
- * BY: Roger Flores
- *
- * COMMENTS: Returns the key which is asked for if possible and
- * advances whichKey. If the key is not available the key advances
- * to the next one. The order of keys is:
- *
- * if sortByCompany:
- * companyKey, nameKey, firstNameKey, uniq ID
- *
- * if !sortByCompany:
- * nameKey, firstNameKey, companyKey (if no name or first name), uniq ID
- *
- *
- *************************************************************/
-
- static void AddrFindKey(AddrPackedDBRecord *r, char **key, UInt *whichKey,
- Int sortByCompany)
- {
- AddrDBRecordFlags fieldFlags;
-
- fieldFlags.allBits = r->flags.allBits;
-
- ErrFatalDisplayIf(*whichKey == 0 || *whichKey == 5, "Bad addr key");
-
- if (sortByCompany)
- {
- if (*whichKey == 1 && fieldFlags.bits.company)
- {
- *whichKey = 2;
- goto returnCompanyKey;
- }
-
- if (*whichKey <= 2 && fieldFlags.bits.name)
- {
- *whichKey = 3;
- goto returnNameKey;
- }
-
- if (*whichKey <= 3 && fieldFlags.bits.firstName)
- {
- *whichKey = 4;
- goto returnFirstNameKey;
- }
- }
- else
- {
- if (*whichKey == 1 && fieldFlags.bits.name)
- {
- *whichKey = 2;
- goto returnNameKey;
- }
-
- if (*whichKey <= 2 && fieldFlags.bits.firstName)
- {
- *whichKey = 3;
- goto returnFirstNameKey;
- }
-
- // For now don't consider company name when sorting by person name
- // unless there isn't a name or firstName
- if (*whichKey <= 3 && fieldFlags.bits.company &&
- !(fieldFlags.bits.name || fieldFlags.bits.firstName))
- {
- *whichKey = 4;
- goto returnCompanyKey;
- }
-
- }
-
- // All possible fields have been tried so return NULL so that
- // the uniq ID is compared.
- *whichKey = 5;
- *key = NULL;
- return;
-
-
-
- returnCompanyKey:
- *key = (char *) &r->companyFieldOffset + r->companyFieldOffset;
- return;
-
-
- returnNameKey:
- *key = &r->firstField;
- return;
-
-
- returnFirstNameKey:
- *key = &r->firstField;
- if (r->flags.bits.name)
- {
- *key += StrLen(*key) + 1;
- }
- return;
-
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrComparePackedRecords
- *
- * DESCRIPTION: Compare two packed records.
- *
- * PARAMETERS: address record 1
- * address record 2
- *
- * RETURNS: -1 if record one is less
- * 1 if record two is less
- *
- * CREATED: 1/14/95
- *
- * BY: Roger Flores
- *
- * COMMENTS: Compare the two records key by key until
- * there is a difference. Return -1 if r1 is less or 1 if r2
- * is less. A zero may be returned if two records
- * seem identical.
- * NULL fields are considered less than others.
- *
- *************************************************************/
-
- static Int AddrComparePackedRecords(AddrPackedDBRecord *r1, AddrPackedDBRecord *r2,
- Int sortByCompany, SortRecordInfoPtr info1, SortRecordInfoPtr info2,
- VoidHand appInfoH)
- {
- UInt whichKey1, whichKey2;
- char *key1, *key2;
- Int result;
-
- whichKey1 = 1;
- whichKey2 = 1;
-
- do {
- AddrFindKey(r1, &key1, &whichKey1, sortByCompany);
- AddrFindKey(r2, &key2, &whichKey2, sortByCompany);
-
- // A key with NULL loses the StrCompare.
- if (key1 == NULL)
- {
- // If both are NULL then return them as equal
- if (key2 == NULL)
- {
- result = 0;
- return result;
- }
- else
- result = -1;
- }
- else
- if (key2 == NULL)
- result = 1;
- else
- {
- result = StrCaselessCompare(key1, key2);
- if (result == 0)
- result = StrCompare(key1, key2);
- }
-
- } while (!result);
-
-
- return result;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrUnpackedSize
- *
- * DESCRIPTION: Return the size of an AddrDBRecordType
- *
- * PARAMETERS: address record
- *
- * RETURNS: the size in bytes
- *
- * CREATED: 1/10/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- static UInt AddrUnpackedSize(AddrDBRecordPtr r)
- {
- UInt size;
- Int index;
-
- size = sizeof (AddrPackedDBRecord) - sizeof (char); // correct
- for (index = firstAddressField; index < addressFieldsCount; index++)
- {
- if (r->fields[index] != NULL)
- size += StrLen(r->fields[index]) + 1;
- }
- return size;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrPack
- *
- * DESCRIPTION: Pack an AddrDBRecordType. Doesn't pack empty strings.
- *
- * PARAMETERS: address record to pack
- * address record to pack into
- *
- * RETURNS: the AddrPackedDBRecord is packed
- *
- * CREATED: 1/10/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- static void AddrPack(AddrDBRecordPtr s, VoidPtr recordP)
- {
- ULong offset;
- AddrDBRecordFlags flags;
- Int index;
- AddrPackedDBRecord* d=0;
- UInt len;
- VoidPtr srcP;
- Char companyFieldOffset;
-
- flags.allBits = 0;
-
- DmWrite(recordP, (ULong)&d->options, &s->options, sizeof(s->options));
- offset = (ULong)&d->firstField;
-
- for (index = firstAddressField; index < addressFieldsCount; index++) {
- if (s->fields[index] != NULL)
- /* if (s->fields[index][0] == '\0')
- {
- // so set the companyFieldOffset or clear it code doesn't fail
- s->fields[index] = NULL;
- }
- else
- */ {
- ErrFatalDisplayIf(s->fields[index][0] == '\0' && index != note,
- "Empty field being added");
- srcP = s->fields[index];
- len = StrLen(srcP) + 1;
- DmWrite(recordP, offset, srcP, len);
- offset += len;
- SetBitMacro(flags.allBits, index);
- }
- }
-
- // Set the flags indicating which fields are used
- DmWrite(recordP, (ULong)&d->flags.allBits, &flags.allBits, sizeof(flags.allBits));
-
- // Set the companyFieldOffset or clear it
- if (s->fields[company] == NULL)
- companyFieldOffset = 0;
- else {
- index = 1;
- if (s->fields[name] != NULL)
- index += StrLen(s->fields[name]) + 1;
- if (s->fields[firstName] != NULL)
- index += StrLen(s->fields[firstName]) + 1;
- companyFieldOffset = index;
- }
- DmWrite(recordP, (ULong)(&d->companyFieldOffset), &companyFieldOffset, sizeof(companyFieldOffset));
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrUnpack
- *
- * DESCRIPTION: Fills in the AddrDBRecord structure
- *
- * PARAMETERS: address record to unpack
- * the address record to unpack into
- *
- * RETURNS: the record unpacked
- *
- * CREATED: 1/14/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- static void AddrUnpack(AddrPackedDBRecord *src, AddrDBRecordPtr dest)
- {
- Int index;
- ULong flags;
- char *p;
-
-
- dest->options = src->options;
- flags = src->flags.allBits;
- p = &src->firstField;
-
-
- for (index = firstAddressField; index < addressFieldsCount; index++)
- {
- // If the flag is set point to the string else NULL
- if (GetBitMacro(flags, index) != 0)
- {
- dest->fields[index] = p;
- p += StrLen(p) + 1;
- }
- else
- dest->fields[index] = NULL;
- }
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrFindSortPosition
- *
- * DESCRIPTION: Return where a record is or should be
- * Useful to find or find where to insert a record.
- *
- * PARAMETERS: address record
- *
- * RETURNS: the size in bytes
- *
- * CREATED: 1/11/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- static UInt AddrFindSortPosition(DmOpenRef dbP, AddrPackedDBRecord *newRecord)
- {
- Int sortByCompany;
- AddrAppInfoPtr appInfoPtr;
-
-
- appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
- sortByCompany = appInfoPtr->misc.sortByCompany;
- MemPtrUnlock(appInfoPtr);
-
- return DmFindSortPosition(dbP, (VoidPtr) newRecord, NULL, (DmComparF *)
- &AddrComparePackedRecords, (Int) sortByCompany);
- }
-
-
-
- /************************************************************
- *
- * FUNCTION: StrCmpMatches
- *
- * DESCRIPTION: Compares two strings and reports the number
- * of matching characters from the start of the strings.
- *
- * PARAMETERS: 2 string pointers
- *
- * RETURNS: number of matching characters between the two strings
- *
- * CREATED: 6/15/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- static Int StrCmpMatches(CharPtr s1, CharPtr s2)
- {
- UInt16 matches;
-
- ErrFatalDisplayIf ( s1 == NULL, "Error NULL string parameter");
- ErrFatalDisplayIf ( s2 == NULL, "Error NULL string parameter");
-
- TxtCaselessCompare(s1, StrLen(s1), &matches, s2, StrLen(s2), NULL);
- return (matches);
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrNewRecord
- *
- * DESCRIPTION: Create a new packed record in sorted position
- *
- * PARAMETERS: database pointer - open db pointer
- * address record - pointer to a record to copy into the DB
- * record index - to be set to the new record's index
- *
- * RETURNS: ##0 if successful, errorcode if not
- * index set if a new record is created.
- *
- * CREATED: 1/10/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- Err AddrNewRecord(DmOpenRef dbP, AddrDBRecordPtr r, UInt *index)
- {
- Handle recordH;
- Err err;
- AddrPackedDBRecord* recordP;
- UInt newIndex;
-
-
- // 1) and 2) (make a new chunk with the correct size)
- recordH = (Handle)DmNewHandle(dbP, (ULong) AddrUnpackedSize(r));
- if (recordH == NULL)
- return dmErrMemError;
-
-
- // 3) Copy the data from the unpacked record to the packed one.
- recordP = MemHandleLock(recordH);
- AddrPack(r, recordP);
-
- // Get the index
- newIndex = AddrFindSortPosition(dbP, recordP);
- MemPtrUnlock(recordP);
-
-
- // 4) attach in place
- err = DmAttachRecord(dbP, &newIndex, recordH, 0);
- if (err)
- MemHandleFree(recordH);
- else
- *index = newIndex;
-
- return err;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrChangeRecord
- *
- * DESCRIPTION: Change a record in the Address Database
- *
- * PARAMETERS: dbP - open database pointer
- * database index
- * address record
- * changed fields
- *
- * RETURNS: ##0 if successful, errorcode if not
- *
- * CREATED: 1/14/95
- *
- * BY: Roger Flores
- *
- * COMMENTS: Records are not stored with extra padding - they
- * are always resized to their exact storage space. This avoids
- * a database compression issue. The code works as follows:
- *
- * 1) get the size of the new record
- * 2) make the new record
- * 3) pack the packed record plus the changes into the new record
- * 4) if the sort position is changes move to the new position
- * 5) attach in position
- *
- * The handle to the record passed doesn't need to be unlocked
- * since that chunk is freed by this routine. It should be discarded.
- *
- *************************************************************/
- Err AddrChangeRecord(DmOpenRef dbP, UInt *index, AddrDBRecordPtr r,
- AddrDBRecordFlags changedFields)
- {
- AddrDBRecordType src;
- Handle srcH;
- Err result;
- Handle recordH=0;
- Handle oldH;
- Int i;
- ULong changes = changedFields.allBits;
- Int sortByCompany;
- AddrAppInfoPtr appInfoPtr;
- Boolean dontMove;
- UInt attributes; // to contain the deleted flag
-
- AddrPackedDBRecord* cmpP;
- AddrPackedDBRecord* recordP;
-
-
- // We do not assume that r is completely valid so we get a valid
- // AddrDBRecordPtr...
- if ((result = AddrGetRecord(dbP, *index, &src, &srcH)) != 0)
- return result;
-
- // and we apply the changes to it.
- src.options = r->options; // copy the phone info
- for (i = firstAddressField; i < addressFieldsCount; i++)
- {
- // If the flag is set point to the string else NULL
- if (GetBitMacro(changes, i) != 0)
- {
- src.fields[i] = r->fields[i];
- RemoveBitMacro(changes, i);
- }
- if (changes == 0)
- break; // no more changes
- }
-
-
- // 1) and 2) (make a new chunk with the correct size)
- recordH = (Handle)DmNewHandle(dbP, (ULong) AddrUnpackedSize(&src));
- if (recordH == NULL)
- {
- MemHandleUnlock(srcH); // undo lock from AddrGetRecord above
- return dmErrMemError;
- }
- recordP = MemHandleLock(recordH);
-
-
- // 3) Copy the data from the unpacked record to the packed one.
- AddrPack(&src, recordP);
-
- // The original record is copied and no longer needed.
- MemHandleUnlock(srcH);
-
-
- // 4) if the sort position changes...
- // Check if any of the key fields have changed
- if ((changedFields.allBits & sortKeyFieldBits) == 0)
- goto attachRecord;
-
-
- // Make sure *index-1 < *index < *index+1, if so it's in sorted
- // order. Leave it there.
- appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
- sortByCompany = appInfoPtr->misc.sortByCompany;
- MemPtrUnlock(appInfoPtr);
-
- if (*index > 0)
- {
- // This record wasn't deleted and deleted records are at the end of the
- // database so the prior record may not be deleted!
- cmpP = MemHandleLock(DmQueryRecord(dbP, *index-1));
- dontMove = (AddrComparePackedRecords (cmpP, recordP, sortByCompany,
- NULL, NULL, 0) == -1);
- MemPtrUnlock(cmpP);
- }
- else
- dontMove = true;
-
-
- if (*index+1 < DmNumRecords (dbP))
- {
- DmRecordInfo(dbP, *index+1, &attributes, NULL, NULL);
- if (attributes & dmRecAttrDelete)
- ; // don't move it after the deleted record!
- else {
- cmpP = MemHandleLock(DmQueryRecord(dbP, *index+1));
- dontMove = dontMove && (AddrComparePackedRecords (recordP, cmpP,
- sortByCompany, NULL, NULL, 0) == -1);
- MemPtrUnlock(cmpP);
- }
- }
-
-
- if (dontMove)
- goto attachRecord;
-
-
-
- // The record isn't in the right position. Move it.
- i = AddrFindSortPosition(dbP, recordP);
- DmMoveRecord(dbP, *index, i);
- if (i > *index) i--;
- *index = i; // return new position
-
-
- // Attach the new record to the old index, the preserves the
- // category and record id.
- attachRecord:
-
- result = DmAttachRecord(dbP, index, recordH, &oldH);
- MemPtrUnlock(recordP);
- if (result) return result;
-
- MemHandleFree(oldH);
- return 0;
- }
-
-
-
- /************************************************************
- *
- * FUNCTION: AddrGetRecord
- *
- * DESCRIPTION: Get a record from the Address Database
- *
- * PARAMETERS: database pointer - open db pointer
- * database index - index of record to lock
- * address record pointer - pointer address structure
- * address record - handle to unlock when done
- *
- * RETURNS: ##0 if successful, errorcode if not
- * The record's handle is locked so that the pointer to
- * strings within the record remain pointing to valid chunk
- * versus the record randomly moving. Unlock the handle when
- * AddrDBRecord is destroyed.
- *
- * CREATED: 1/14/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- Err AddrGetRecord(DmOpenRef dbP, UInt index, AddrDBRecordPtr recordP,
- Handle *recordH)
- {
- AddrPackedDBRecord *src;
-
- *recordH = DmQueryRecord(dbP, index);
- src = (AddrPackedDBRecord *) MemHandleLock(*recordH);
- if (src == NULL)
- return dmErrIndexOutOfRange;
-
- AddrUnpack(src, recordP);
-
- return 0;
- }
-
-
- /***********************************************************************
- *
- * FUNCTION: RecordContainsData
- *
- * DESCRIPTION: Checks the record returns true if it contains any data.
- *
- * PARAMETERS: recordP - a pointer to an address record
- *
- * RETURNED: true if one of the fields has data
- *
- * REVISION HISTORY:
- * Name Date Description
- * ---- ---- -----------
- * rsf 12/3/97 Initial Revision
- *
- ***********************************************************************/
- Boolean RecordContainsData (AddrDBRecordPtr recordP)
- {
- UInt i;
-
-
- // Look for a field which isn't empty
- for (i = firstAddressField; i < addressFieldsCount; i++)
- {
- if (recordP->fields[i] != NULL)
- return true;
- }
-
- return false;
- }
-
-
- /***********************************************************************
- *
- * FUNCTION: RecordContainsField
- *
- * DESCRIPTION: Check if a packed record contains a desired field.
- *
- * PARAMETERS: recordP - pointer to the record to search
- * field - type of field to find.
- *
- * RETURNED: true if the record contains the field.
- *
- * REVISION HISTORY:
- * Name Date Description
- * ---- ---- -----------
- * Roger 7/9/96 Initial Revision
- *
- ***********************************************************************/
- static Boolean RecordContainsField(AddrPackedDBRecord *packedRecordP,
- AddressLookupFields field, UIntPtr phoneP, Int direction,
- AddressFields lookupFieldMap[])
- {
- int index;
- int stopIndex;
- int phoneType;
-
-
- switch (field)
- {
- case addrLookupSortField:
- return packedRecordP->flags.allBits & sortKeyFieldBits;
-
- case addrLookupListPhone:
- return GetBitMacro(packedRecordP->flags.allBits, firstPhoneField +
- packedRecordP->options.phones.displayPhoneForList);
-
- case addrLookupNoField:
- return true;
-
- default:
- if (!IsPhoneLookupField(field))
- return GetBitMacro(packedRecordP->flags.allBits, lookupFieldMap[field]) != 0;
-
- phoneType = field - addrLookupWork;
- index = firstPhoneField + *phoneP;
- if (direction == dmSeekForward)
- stopIndex = lastPhoneField + direction;
- else
- stopIndex = firstPhoneField + direction;
-
- while (index != stopIndex)
- {
- // If the phone field is the type requested and it's not empty
- // return it.
- if (GetPhoneLabel(packedRecordP, index) == phoneType &&
- GetBitMacro(packedRecordP->flags.allBits, index))
- {
- *phoneP = index - firstPhoneField;
- return true;
- }
- index += direction;
- }
-
- // The phone type wasn't used.
- if (direction == dmSeekForward)
- *phoneP = 0; // Reset for the next record
- else
- *phoneP = numPhoneFields - 1; // Reset for the next record
-
- return false;
- }
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrChangeSortOrder
- *
- * DESCRIPTION: Change the Address Database's sort order
- *
- * PARAMETERS: dbP - open database pointer
- * TRUE if sort by company
- *
- * RETURNS: nothing
- *
- * CREATED: 1/17/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- Err AddrChangeSortOrder(DmOpenRef dbP, Boolean sortByCompany)
- {
- AddrAppInfoPtr appInfoPtr;
- AddrAppInfoPtr nilP=0;
- AddrDBMisc misc;
-
-
- appInfoPtr = (AddrAppInfoPtr) AddrAppInfoGetPtr(dbP);
- misc = appInfoPtr->misc;
- misc.sortByCompany = sortByCompany;
- DmWrite(appInfoPtr, (ULong) &nilP->misc, &misc, sizeof(misc));
- MemPtrUnlock(appInfoPtr);
-
- DmQuickSort(dbP, (DmComparF *) &AddrComparePackedRecords, (Int) sortByCompany);
- return 0;
- }
-
-
- /***********************************************************************
- *
- * FUNCTION: AddrLookupSeekRecord
- *
- * DESCRIPTION: Given the index of a record, scan
- * forewards or backwards for displayable records.
- *
- * PARAMETERS: indexP - pointer to the index of a record to start from;
- * the index of the record sought is returned in
- * this parameter.
- *
- * offset - number of records to skip:
- * 0 - mean seek from the current record to the
- * next display record, if the current record is
- * a display record, its index is retuned.
- * 1 - mean seek foreward, skipping one displayable
- * record
- * -1 - means seek backwards, skipping one
- * displayable record
- *
- *
- * RETURNED: true if a displayable record was found.
- *
- * REVISION HISTORY:
- * Name Date Description
- * ---- ---- -----------
- * Roger 7/9/96 Initial Revision
- *
- ***********************************************************************/
- extern Boolean AddrLookupSeekRecord (DmOpenRef dbP, UIntPtr indexP,
- UIntPtr phoneP, Int offset, Int direction,
- AddressLookupFields field1, AddressLookupFields field2,
- AddressFields lookupFieldMap[])
- {
- UInt index;
- UInt oldIndex;
- UInt count;
- UInt numRecords;
- Handle recordH;
- Boolean match;
- UInt phone;
- Boolean searchPhones;
- AddrPackedDBRecord *packedRecordP;
-
-
- ErrFatalDisplayIf ( (direction != dmSeekForward) && (direction != dmSeekBackward),
- "Bad Param");
-
- ErrFatalDisplayIf ( (offset < 0), "Bad param");
-
-
- index = *indexP;
- phone = *phoneP;
-
- searchPhones = IsPhoneLookupField(field1) || IsPhoneLookupField(field2);
-
- numRecords = DmNumRecords(dbP);
-
- if (index >= numRecords)
- {
- if (direction == dmSeekForward)
- return false;
- else
- index = numRecords - 1;
- }
-
-
- // Moving forward?
- if (direction == dmSeekForward )
- count = numRecords - index;
- else
- count = index + 1;
-
- // Loop through the records
- while (count--) {
-
- // Make sure the current record isn't hidden. If so skip it and find the
- // next non hidden record. Decrease the record count to search by the number
- // of records skipped.
- oldIndex = index;
- if (DmSeekRecordInCategory (dbP, &index, 0, direction, dmAllCategories))
- {
- // There are no more records.
- break;
- }
- if (index != oldIndex)
- {
- if (direction == dmSeekForward)
- count -= index - oldIndex;
- else
- count -= oldIndex - index;
- }
-
- recordH = DmQueryRecord(dbP, index);
-
- // If we have found a deleted record stop the search.
- if (!recordH)
- break;
-
- packedRecordP = MemHandleLock(recordH);
- if (!packedRecordP)
- goto Exit;
-
- match = RecordContainsField(packedRecordP, field1, &phone, direction, lookupFieldMap) &&
- RecordContainsField(packedRecordP, field2, &phone, direction, lookupFieldMap);
-
- MemHandleUnlock(recordH);
-
- if (match)
- {
- *indexP = index;
- *phoneP = phone;
- if (offset == 0) return true;
- offset--;
- }
-
- // Look for another phone in this record if one was found or
- // else look at the next record.
- if (searchPhones && match)
- {
- phone += direction;
- // We their are no more phones to search so advance to next record
- if (phone == 0xffff || numPhoneFields <= phone)
- {
- if (direction == dmSeekForward)
- phone = 0;
- else
- phone = numPhoneFields - 1;
-
- index += direction;
- }
- else
- {
- // Since we are going to search this record again bump the count up
- // by one. This loop is supposed to loop once per record to search.
- count++;
- }
- }
- else
- index += direction;
-
- }
-
- return false;
-
- Exit:
- ErrDisplay("Err seeking rec");
-
- return false;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrLookupString
- *
- * DESCRIPTION: Return which record contains the most of
- * the string passed. If no string is passed or there
- * aren't any records then false is returned.
- *
- * PARAMETERS: address record
- * key - string to lookup record with
- * sortByCompany - how the db is sorted
- * category - the category to search in
- * recordP - to contain the record found
- * completeMatch - true if a record contains all
- * of the key
- *
- * RETURNS: the record in recordP or false
- * completeMatch - true if a record contains all
- * of the key
- *
- * CREATED: 6/15/95
- *
- * BY: Roger Flores
- *
- *************************************************************/
- Boolean AddrLookupString(DmOpenRef dbP, CharPtr key,
- Boolean sortByCompany, UInt category, UIntPtr recordP, Boolean *completeMatch)
- {
- Int numOfRecords;
- Handle rH;
- AddrPackedDBRecord* r;
- UInt kmin, probe, probe2, i; // all positions in the database.
- Int result; // result of comparing two records
- UInt whichKey;
- char* recordKey;
- UInt matches1, matches2;
-
-
- // If there isn't a key to search with stop the with the first record.
- if (key == NULL || *key == '\0')
- {
- *completeMatch = true;
- return false;
- }
-
- numOfRecords = DmNumRecords(dbP);
- if (numOfRecords == 0)
- return false;
-
- result = 0;
- kmin = probe = 0;
- rH = 0;
-
-
- while (numOfRecords > 0)
- {
- i = numOfRecords / 2;
- probe = kmin + i;
-
-
- // Compare the two records. Treat deleted records as greater.
- // If the records are equal look at the following position.
- if (rH)
- MemHandleUnlock(rH);
- rH = DmQueryRecord(dbP, probe);
- if (rH == 0)
- {
- result = -1; // Delete record is greater
- }
- else
- {
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "Addr bsearch: data somehow missing");
-
-
- // Compare the string to the first sort key only
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
-
- if (recordKey == NULL)
- result = 1;
- else
- result = StrCaselessCompare(key, recordKey);
-
-
- // If equal stop here! We don't want the position after.
- if (result == 0)
- goto findRecordInCategory;
- }
-
-
- ErrFatalDisplayIf(result == 0, "Impossible bsearch state");
-
- // More likely than < 0 because of deleted records
- if (result < 0)
- numOfRecords = i;
- else
- {
- kmin = probe + 1;
- numOfRecords = numOfRecords - i - 1;
- }
- }
-
- if (result >= 0)
- probe++;
-
- findRecordInCategory:
- if (rH)
- MemHandleUnlock(rH);
-
- // At this point probe is the position where the string could be
- // inserted. It is in between two entries. Neither the record
- // before or after may have ANY letters in common, especially after
- // those records in other catergories are skipped. Go with the
- // record that has the most letters in common.
-
-
- // Make sure the record returned is of the same category.
- // If not return the first prior record of the same category.
- probe2 = probe;
- if (!DmSeekRecordInCategory (dbP, &probe, 0, dmSeekForward, category))
- {
- // Now count the number of matching characters in probe
- rH = DmQueryRecord(dbP, probe); // No deleted record possible
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "Addr bsearch: data somehow missing");
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
- if (recordKey == NULL)
- matches1 = 0;
- else
- matches1 = StrCmpMatches(key, recordKey);
-
- MemHandleUnlock(rH);
- }
- else
- {
- // No record in this category was found or probe is past all
- // records in this category. Either way there aren't any matching
- // letters.
- matches1 = 0;
- }
-
-
-
- // Sometimes the record before has more matching letters. Check it.
- // Passing DmSeekRecordInCategory an offset of 1 doesn't work
- // when probe is at the end of the database and there isn't at least
- // one record to skip.
- probe2 = probe - 1;
- if (probe == 0 ||
- DmSeekRecordInCategory (dbP, &probe2, 0, dmSeekBackward, category))
- {
- if (matches1 > 0)
- {
- // Go with probe because they have at least some letters in common.
- *recordP = probe; //
- *completeMatch = (matches1 == StrLen(key));
- return true;
- }
- else
- {
- // probe has no letters in common and nothing earlier in this category
- // was found so this is a failed lookup.
- *completeMatch = false;
- return false;
- }
- }
-
-
- // Now count the number of matching characters in probe2
- rH = DmQueryRecord(dbP, probe2); // No deleted record possible
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "Addr bsearch: data somehow missing");
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
- if (recordKey == NULL)
- matches2 = 0;
- else
- matches2 = StrCmpMatches(key, recordKey);
- MemHandleUnlock(rH);
-
-
- // Now, return the probe which has the most letters in common.
- if (matches1 > matches2)
- {
- *completeMatch = (matches1 == StrLen(key));
- *recordP = probe;
- }
- else
- if (matches1 == 0 && matches2 == 0)
- {
- *completeMatch = false;
- return false; // no item with same first letter found
- }
- else
- {
- // The first item matches as much or more as the second item
- *recordP = probe2;
-
- // If the prior item in the category has the same number of
- // matching letters use it instead. Repeat to find the
- // earliest such match.
- while (!DmSeekRecordInCategory (dbP, &probe2, 1, dmSeekBackward, category))
- {
- rH = DmQueryRecord(dbP, probe2);
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "Addr bsearch: data somehow missing");
-
- // Compare the string to the first sort key only
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
-
- if (recordKey == NULL)
- matches1 = 0;
- else
- matches1 = StrCmpMatches(key, recordKey);
-
- MemHandleUnlock(rH);
-
- if (matches1 == matches2)
- *recordP = probe2;
- else
- break;
- }
-
- *completeMatch = (matches2 == StrLen(key));
- }
-
- return true;
- }
-
-
- /************************************************************
- *
- * FUNCTION: AddrLookupLookupString
- *
- * DESCRIPTION: Return which record contains the most of
- * the string passed. If no string is passed or there
- * aren't any records then false is returned.
- *
- * PARAMETERS: address record
- * key - string to lookup record with
- * sortByCompany - how the db is sorted
- * vars - Lookup variables
- * recordP - to contain the record found
- * phoneP - to contain the phone found
- * completeMatch - true if a record contains all
- * of the key
- *
- * RETURNS: the record in recordP or false
- * completeMatch - true if a record contains all
- * of the key
- *
- * RETURNED: false is return if a displayable record was not found.
- *
- * REVISION HISTORY:
- * Name Date Description
- * ---- ---- -----------
- * Roger 7/19/96 Initial Revision
- *
- *************************************************************/
- extern Boolean AddrLookupLookupString(DmOpenRef dbP, CharPtr key,
- Boolean sortByCompany, AddressLookupFields field1,
- AddressLookupFields field2, UIntPtr recordP, UIntPtr phoneP,
- AddressFields lookupFieldMap[], Boolean *completeMatch,
- Boolean *uniqueMatch)
- {
- Int numOfRecords;
- Handle rH;
- AddrPackedDBRecord* r;
- // UInt kmin, i; // all positions in the database.
- UInt probe, probe2 ; // all positions in the database.
- UInt phoneProbe, phoneProbe2;
- // Int result; // result of comparing two records
- UInt whichKey;
- char* recordKey;
- UInt matches1, matches2;
- AddressFields searchField;
- AddrDBRecordFlags searchFieldFlag;
-
-
- *uniqueMatch = false;
- // If there isn't a key to search with stop the with the first record.
- if (key == NULL || *key == '\0')
- {
- *completeMatch = true;
- return false;
- }
-
- numOfRecords = DmNumRecords(dbP);
- if (numOfRecords == 0)
- return false;
-
- // Performing a lookup on the sort field allows the use a binary search which
- // takes advantage of the ordered field.
- if (field1 == addrLookupSortField)
- {
- // Perform the standard lookup on the sort fields looking at all categories.
- if (!AddrLookupString(dbP, key, sortByCompany, dmAllCategories,
- recordP, completeMatch))
- return false; // nothing matched
-
-
- // At this point probe is the position where the string could be
- // inserted. It is in between two entries. Neither the record
- // before or after may have ANY letters in common, especially after
- // those records in other catergories are skipped. Go with the
- // record that has the most letters in common.
-
-
- // Make sure the record returned is of the same category.
- // If not return the first prior record of the same category.
- probe2 = probe = *recordP;
- phoneProbe2 = phoneProbe = 0;
- if (AddrLookupSeekRecord (dbP, &probe, &phoneProbe, 0, dmSeekForward,
- field1, field2, lookupFieldMap))
- {
- // Now count the number of matching characters in probe
- rH = DmQueryRecord(dbP, probe); // No deleted record possible
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "AddrLookup bsearch: data somehow missing");
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
- if (recordKey == NULL)
- matches1 = 0;
- else
- matches1 = StrCmpMatches(key, recordKey);
-
- MemHandleUnlock(rH);
- }
- else
- {
- // No record in this category was found or probe is past all
- // records in this category. Either way there aren't any matching
- // letters.
- matches1 = 0;
- }
-
-
- *uniqueMatch = true;
-
-
- // Sometimes the record before has more matching letters. Check it.
- // Passing DmSeekRecordInCategory an offset of 1 doesn't work
- // when probe is at the end of the database and there isn't at least
- // one record to skip.
- probe2 = probe - 1;
- if (probe == 0 ||
- !AddrLookupSeekRecord (dbP, &probe2, &phoneProbe2, 0, dmSeekBackward,
- field1, field2, lookupFieldMap))
- {
- // There isn't an earlier record. Try to find a following record.
- probe2 = probe + 1;
- phoneProbe2 = phoneProbe;
- if (!AddrLookupSeekRecord (dbP, &probe2, &phoneProbe2, 0, dmSeekForward,
- field1, field2, lookupFieldMap))
- {
- // There isn't a following record. Try to use the probe.
- if (matches1 > 0)
- {
- // Go with probe because they have at least some letters in common.
- *recordP = probe; //
- *phoneP = phoneProbe;
- *completeMatch = (matches1 == StrLen(key));
- return true;
- }
- else
- {
- // probe has no letters in common and nothing earlier in this category
- // was found so this is a failed lookup.
- *completeMatch = false;
- return false;
- }
- }
- }
-
-
- // Now count the number of matching characters in probe2
- rH = DmQueryRecord(dbP, probe2); // No deleted record possible
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "AddrLookup bsearch: data somehow missing");
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
- if (recordKey == NULL)
- matches2 = 0;
- else
- matches2 = StrCmpMatches(key, recordKey);
- MemHandleUnlock(rH);
-
-
- // Now, return the probe which has the most letters in common.
- if (matches1 > matches2)
- {
- *completeMatch = (matches1 == StrLen(key));
- *recordP = probe;
- *phoneP = phoneProbe;
-
- // If the next item has the same number of
- // matching letters then the match is not unique.
- probe2 = probe;
- phoneProbe2 = phoneProbe;
- if (AddrLookupSeekRecord (dbP, &probe2, &phoneProbe2, 1, dmSeekForward,
- field1, field2, lookupFieldMap))
- {
- rH = DmQueryRecord(dbP, probe2);
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "AddrLookup bsearch: data somehow missing");
-
- // Compare the string to the first sort key only
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
-
- if (recordKey == NULL)
- matches2 = 0;
- else
- matches2 = StrCmpMatches(key, recordKey);
-
- MemHandleUnlock(rH);
-
- if (matches1 <= matches2)
- {
- *uniqueMatch = false;
- }
- }
- }
- else
- if (matches1 == 0 && matches2 == 0)
- {
- *completeMatch = false;
- *uniqueMatch = false;
- return false; // no item with same first letter found
- }
- else
- {
- // The first item matches as much or more as the second item
- *recordP = probe2;
- *phoneP = phoneProbe2;
-
- // If the prior item in the category has the same number of
- // matching letters use it instead. Repeat to find the
- // earliest such match.
- while (AddrLookupSeekRecord (dbP, &probe2, &phoneProbe2, 1, dmSeekBackward,
- field1, field2, lookupFieldMap))
- {
- rH = DmQueryRecord(dbP, probe2);
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "AddrLookup bsearch: data somehow missing");
-
- // Compare the string to the first sort key only
- whichKey = 1;
- AddrFindKey(r, &recordKey, &whichKey, sortByCompany);
-
- if (recordKey == NULL)
- matches1 = 0;
- else
- matches1 = StrCmpMatches(key, recordKey);
-
- MemHandleUnlock(rH);
-
- if (matches1 == matches2)
- {
- *recordP = probe2;
- *phoneP = phoneProbe2;
- }
- else
- break;
- }
-
- *completeMatch = (matches2 == StrLen(key));
- *uniqueMatch = false;
- }
-
- return true;
-
- }
- else
- {
- // Peform a lookup based on unordered data. This gets real slow with lots of data
- // Because to check for uniqueness we must search every record. This means on average
- // this lookup is twice as slow as it would be it it could stop with the first match.
- AddrDBRecordType record;
-
-
- *completeMatch = false;
-
- matches1 = 0; // treat this as the most matches
-
- // cache these values
- searchField = lookupFieldMap[field1];
- searchFieldFlag.allBits = BitAtPosition(field1);
-
- // Start with the first record and look at each record until there are no more.
- // Look for the record with the most number of matching records. Even if we found
- // a record containing all the record we are searching for we must still look
- // for one more complete match to confirm or deny uniqueness of the match.
- probe2 = 0;
- phoneProbe2 = 0;
- while (AddrLookupSeekRecord (dbP, &probe2, &phoneProbe2, 1, dmSeekForward,
- field1, field2, lookupFieldMap))
- {
- rH = DmQueryRecord(dbP, probe2);
- r = (AddrPackedDBRecord *) MemHandleLock(rH);
- ErrFatalDisplayIf(r == 0, "AddrLookup bsearch: data somehow missing");
-
- // Compare the string to the search field
- if (r->flags.allBits & searchFieldFlag.allBits)
- {
- AddrUnpack(r, &record);
- recordKey = record.fields[searchField];
-
- if (recordKey == NULL)
- matches2 = 0;
- else
- matches2 = StrCmpMatches(key, recordKey);
- }
- else
- {
- matches2 = 0;
- }
-
- MemHandleUnlock(rH);
-
- if (matches2 > matches1)
- {
- matches1 = matches2; // the most matches so far
-
- *recordP = probe2; // return the best record
- *phoneP = phoneProbe2;
-
- *completeMatch = (matches2 == StrLen(key));
- }
- // Did we find another record which is a complete match?
- else if (matches2 > 0 &&
- matches1 == matches2 &&
- *completeMatch)
- {
- *uniqueMatch = false;
- return true;
- }
- else
- {
- // The record is a matching failure. Since AddrLookupSeekRecord is going
- // to return this record again for every phone field we cheat by specifying
- // the last phone field to skip all other entries.
- // phoneProbe2 = numPhoneFields - 1;
- }
- }
-
-
- // Was at least one record found with at least one matching character?
- if (matches1 > 0)
- {
- // At this point every record was searched and no other match was found.
- *uniqueMatch = true;
-
- return true;
- }
- }
-
- return false;
- }
-
-